package com.ds.infra.rpc.core.invoke;

import com.ds.infra.rpc.core.loadbalance.LoadBalance;
import com.ds.infra.rpc.core.loadbalance.LoadBalanceFactory;
import com.ds.infra.rpc.core.protocol.ProtocolBuilder;
import com.ds.infra.rpc.core.protocol.RpcProtocol;
import com.ds.infra.rpc.core.protocol.RpcRequest;
import com.ds.infra.rpc.core.protocol.RpcResponse;
import com.ds.infra.rpc.core.transport.client.NettyClient;
import com.ds.infra.rpc.registry.model.ServiceInstance;
import com.ds.infra.rpc.registry.service.ServiceDiscovery;

import java.util.List;
import java.util.concurrent.CompletableFuture;

/**
 * 作用：RPC服务调用的核心实现类
 *
 * @author WeiShaoying
 */

public class ServiceInvoker {

    private final ServiceDiscovery serviceDiscovery;
    private final LoadBalanceFactory loadBalanceFactory;
    private final NettyClientPool clientPool; // 连接池

    public ServiceInvoker(ServiceDiscovery serviceDiscovery,
                          LoadBalanceFactory loadBalanceFactory,
                          NettyClientPool clientPool) {
        this.serviceDiscovery = serviceDiscovery;
        this.loadBalanceFactory = loadBalanceFactory;
        this.clientPool = clientPool;
    }
    public CompletableFuture<RpcResponse> invoke(String serviceName,
                                                 String version,
                                                 RpcRequest request) {
        try {
            // 1. 服务发现
            List<ServiceInstance> instances = serviceDiscovery.discoverInstance(
                    buildServiceKey(serviceName, version));

            // 2. 负载均衡选择
            LoadBalance loadBalance = loadBalanceFactory.getLoadBalance(serviceName);
            ServiceInstance instance = loadBalance.selectInstance(instances);

            // 3. 获取客户端连接
            NettyClient client = clientPool.getClient(instance.getHost(), instance.getPort());

            // 4. 构建协议并发送
            RpcProtocol<RpcRequest> protocol = ProtocolBuilder.buildProtocol(request);
            return client.send(protocol);

        } catch (Exception e) {
            CompletableFuture<RpcResponse> future = new CompletableFuture<>();
            future.completeExceptionally(e);
            return future;
        }
    }

    private String buildServiceKey(String serviceName, String version) {
        return serviceName + ":" + version;
    }
}
